﻿using Furion;
using Furion.DatabaseAccessor;
using Furion.DatabaseAccessor.Extensions;
using Furion.DataEncryption;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.EventBus;
using Furion.FriendlyException;
using Furion.Localization;
using Mapster;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using PrivateTracker.Service.Core;
using System.ComponentModel.DataAnnotations;
using UAParser;

namespace PrivateTracker.Service.Application;
/// <summary>
/// 登录授权相关服务
/// </summary>
[ApiDescriptionSettings(Name = "Auth", Order = 99998)]
public class AuthService : IAuthService, IDynamicApiController, ITransient
{
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IRepository<SysTenant, MultiTenantDbContextLocator> _sysTenantRep;
    private readonly IRepository<SysUser> _sysUserRep; // 用户表仓储
    private readonly IUserManager _userManager; // 用户管理

    private readonly ISysUserService _sysUserService; // 系统用户服务
    private readonly ISysRoleService _sysRoleService; // 系统角色服务
    private readonly ISysMenuService _sysMenuService; // 系统菜单服务
    private readonly ISysAppService _sysAppService; // 系统应用服务
    private readonly IClickWordCaptcha _captchaHandle; // 验证码服务
    private readonly ISysConfigService _sysConfigService; // 验证码服务
    private readonly IEventPublisher _eventPublisher;
    /// <summary>
    /// 
    /// </summary>
    /// <param name="sysTenantRep"></param>
    /// <param name="sysUserRep"></param>
    /// <param name="httpContextAccessor"></param>
    /// <param name="userManager"></param>
    /// <param name="sysUserService"></param>
    /// <param name="sysRoleService"></param>
    /// <param name="sysMenuService"></param>
    /// <param name="sysAppService"></param>
    /// <param name="captchaHandle"></param>
    /// <param name="sysConfigService"></param>
    /// <param name="eventPublisher"></param>
    public AuthService(
        IRepository<SysTenant, MultiTenantDbContextLocator> sysTenantRep,
        IRepository<SysUser> sysUserRep,
        IHttpContextAccessor httpContextAccessor,
        IUserManager userManager, ISysUserService sysUserService, ISysRoleService sysRoleService,
        ISysMenuService sysMenuService, ISysAppService sysAppService, IClickWordCaptcha captchaHandle,
        ISysConfigService sysConfigService, IEventPublisher eventPublisher)
    {
        _sysTenantRep = sysTenantRep;
        _sysUserRep = sysUserRep;
        _httpContextAccessor = httpContextAccessor;
        _userManager = userManager;
        _sysUserService = sysUserService;
        _sysRoleService = sysRoleService;
        _sysMenuService = sysMenuService;
        _sysAppService = sysAppService;
        _captchaHandle = captchaHandle;
        _sysConfigService = sysConfigService;
        _eventPublisher = eventPublisher;
    }
    /// <summary>
    /// 用户注册
    /// </summary>
    /// <param name="registInput"></param>
    /// <returns></returns>
    [HttpPost("/register")]
    [AllowAnonymous]
    public async Task<UserOutput> RegisterAsync(RegistInput registInput)
    {
        var isExist = await _sysUserRep.DetachedEntities.AnyAsync(u => u.Account.ToLower().Equals(registInput.Account.Trim().ToLower()) || u.Email.ToLower().Equals(registInput.Email.Trim().ToLower()));
        if (isExist)
            throw Oops.Oh(ErrorCode.D1003);
        var tenant = await _sysTenantRep.Where(p => p.IsDeleted == false).FirstOrDefaultAsync();
        var user = registInput.Adapt<SysUser>();
        // user.AdminType = AdminType.None; // 非管理员
        user.Password = MD5Encryption.Encrypt(registInput.Password);
        user.TenantId = tenant.Id;

        var registUser = (await user.InsertNowAsync()).Entity.Adapt<UserOutput>();

        return await Task.FromResult(registUser);
    }
    /// <summary>
    /// 用户登录
    /// </summary>
    /// <param name="input"></param>
    /// <remarks>默认用户名/密码：admin/admin</remarks>
    /// <returns></returns>
    [HttpPost("/login")]
    [AllowAnonymous]
    public string LoginAsync([Required] LoginInput input)
    {
        // 获取加密后的密码
        var encryptPasswod = MD5Encryption.Encrypt(input.Password);

        // 判断用户名和密码是否正确 忽略全局过滤器
        var user = _sysUserRep.Include(u => u.SysRoles, false, true)
            .Where(u => ((u.Account.Trim().ToLower() == input.AccountOrEmail.Trim().ToLower()) || u.Email.Trim().ToLower() == input.AccountOrEmail.Trim().ToLower()) && u.Password.Equals(encryptPasswod) && !u.IsDeleted)
            .FirstOrDefault();
        _ = user ?? throw Oops.Oh(ErrorCode.D1000);

        // 验证账号是否被冻结
        if (user.Status == CommonStatus.DISABLE)
            throw Oops.Oh(ErrorCode.D1017);
        // 生成Token令牌
        //var accessToken = await _jwtBearerManager.CreateTokenAdmin(user);

        var sysRoles = user.SysRoles.OrderBy(p => p.RoleType).ToList();
        var accessToken = JWTEncryption.Encrypt(new Dictionary<string, object>
            {
                {ClaimConst.CLAINM_USERID, user.Id},
                {ClaimConst.TENANT_ID, user.TenantId},
                {ClaimConst.CLAINM_ACCOUNT, user.Account},
                {ClaimConst.CLAINM_NICKNAME, user.NickName},
                {ClaimConst.CLAINM_ROLETYPE,sysRoles.Any() ? sysRoles.FirstOrDefault().RoleType : RoleTypeEnum.LEVEL_GROUP},
                {ClaimConst.PASS_KEY, user.PassKey}
            });

        // 设置Swagger自动登录
        _httpContextAccessor.HttpContext.SigninToSwagger(accessToken);

        // 生成刷新Token令牌
        var refreshToken =
            JWTEncryption.GenerateRefreshToken(accessToken, App.GetOptions<RefreshTokenSettingOptions>().ExpiredTime);

        // 设置刷新Token令牌
        _httpContextAccessor.HttpContext.Response.Headers["x-access-token"] = refreshToken;

        return accessToken;
    }

    /// <summary>
    /// 获取当前登录用户信息
    /// </summary>
    /// <returns></returns>
    [HttpGet("/getLoginUser")]
    public async Task<LoginOutput> GetLoginUserAsync()
    {
        var user = _userManager.User;
        if (user == null)
            throw Oops.Oh(ErrorCode.D1011);
        var userId = user.Id;

        var httpContext = _httpContextAccessor.HttpContext;
        var loginOutput = user.Adapt<LoginOutput>();

        loginOutput.LastLoginTime = user.LastLoginTime = DateTimeOffset.Now;
        loginOutput.LastLoginIp = user.LastLoginIp = httpContext.GetRequestIPv4();

        //var ipInfo = IpTool.Search(loginOutput.LastLoginIp);
        //loginOutput.LastLoginAddress = ipInfo.Country + ipInfo.Province + ipInfo.City + "[" + ipInfo.NetworkOperator + "][" + ipInfo.Latitude + ipInfo.Longitude + "]";

        var client = Parser.GetDefault().Parse(httpContext.Request.Headers["User-Agent"]);
        loginOutput.LastLoginBrowser = client.UA.Family + client.UA.Major;
        loginOutput.LastLoginOs = client.OS.Family + client.OS.Major;

        // 角色信息
        loginOutput.Roles = await _sysRoleService.GetUserRoleList(userId);

        // 权限信息
        loginOutput.Permissions = await _sysMenuService.GetLoginPermissionList(userId);

        // 系统所有权限信息
        loginOutput.AllPermissions = await _sysMenuService.GetAllPermissionList();

        // 数据范围信息(机构Id集合)
        loginOutput.DataScopes = await _sysUserService.GetUserDataScopeIdList(userId);

        // 具备应用信息（多系统，默认激活一个，可根据系统切换菜单）,返回的结果中第一个为激活的系统
        loginOutput.Apps = await _sysAppService.GetLoginApps(userId);

        // 菜单信息
        if (loginOutput.Apps.Count > 0)
        {
            var activeApp = loginOutput.Apps.FirstOrDefault(u => u.Active == YesOrNot.Y.ToString());
            var defaultActiveAppCode = activeApp != null ? activeApp.Code : loginOutput.Apps.FirstOrDefault().Code;
        }

        // 更新用户最后登录Ip和时间
        await _sysUserRep.UpdateIncludeAsync(user, new[] { nameof(SysUser.LastLoginIp), nameof(SysUser.LastLoginTime) });

        // 增加登录日志
        await _eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog",
            new SysLogVis
            {
                Name = loginOutput.NickName,
                Success = YesOrNot.Y,
                Message = L.Text["登录成功"].ToString(),
                Ip = loginOutput.LastLoginIp,
                Browser = loginOutput.LastLoginBrowser,
                Os = loginOutput.LastLoginOs,
                VisType = LoginType.LOGIN,
                VisTime = loginOutput.LastLoginTime,
                Account = loginOutput.Account
            }));
        return loginOutput;
    }

    /// <summary>
    /// 退出
    /// </summary>
    /// <returns></returns>
    [HttpGet("/logout")]
    [AllowAnonymous]
    public async Task LogoutAsync()
    {
        var ip = _httpContextAccessor.HttpContext.GetRequestIPv4();
        _httpContextAccessor.HttpContext.SignoutToSwagger();
        //_httpContextAccessor.HttpContext.Response.Headers["access-token"] = "invalid token";

        // 增加退出日志
        await _eventPublisher.PublishAsync(new ChannelEventSource("Create:VisLog",
            new SysLogVis
            {
                Name = _userManager.NickName,
                Success = YesOrNot.Y,
                Message = "退出成功",
                VisType = LoginType.LOGOUT,
                VisTime = DateTimeOffset.Now,
                Account = _userManager.Account,
                Ip = ip
            }));
    }

    /// <summary>
    /// 获取验证码开关
    /// </summary>
    /// <returns></returns>
    [HttpGet("/getCaptchaOpen")]
    [AllowAnonymous]
    public async Task<bool> GetCaptchaOpen()
    {
        return await _sysConfigService.GetCaptchaOpenFlag();
    }

    /// <summary>
    /// 获取验证码（默认点选模式）
    /// </summary>
    /// <returns></returns>
    [HttpPost("/captcha/get")]
    [AllowAnonymous]
    [NonUnify]
    public async Task<dynamic> GetCaptcha()
    {
        // 图片大小要与前端保持一致（坐标范围）
        return await Task.FromResult(_captchaHandle.CreateCaptchaImage(_captchaHandle.RandomCode(4), 310, 155));
    }

    /// <summary>
    /// 校验验证码
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpPost("/captcha/check")]
    [AllowAnonymous]
    [NonUnify]
    public async Task<dynamic> VerificationCode(ClickWordCaptchaInput input)
    {
        return await Task.FromResult(_captchaHandle.CheckCode(input));
    }
}